import Page from "@/models/core/Page";
import {type TablePaginationConfig} from "ant-design-vue/es";
import type {ColumnType, FilterValue, SorterResult, TableCurrentDataSource} from "ant-design-vue/lib/table/interface";
import Sort, {Direction, Order} from "@/models/core/Sort";
import type Named from "@/models/core/Named";
import  {type Identifier} from "@/models/core/Named";
import ObjectSearch from "@/models/core/ObjectSearch";
import {BindThis} from "../../core/BindThis";
import ModalController from "../CatModel/ModalController";
import type {ModalConfig, ModalExpose} from "../CatModel/ModalController";
import CrudApi from "../../core/CrudApi";
import {type Ref, unref} from "vue";
import Pageable from "@/models/core/Pageable";
import {type CatColumn} from "@/components/CatTable/column/CatColumn";
import {type ActionColumn, type ActionItem} from "@/components/CatTable/column/ActionColumn";
import type {ClassConstructor} from "class-transformer";

export interface CrudConfig<V extends Named, T, S extends ObjectSearch> {
    // 数据列
    columns?: CatColumn[];
    // 操作列
    actions?: ActionColumn[];

    // 表格加载过程
    loading?: boolean;
    // 对象记录页
    page?: Page<V>;
    // 本地数据缓存
    contents?: V[],
    // 选中的记录
    selectedRows?: V[];
    // 选中记录的key值集合
    selectedRowKeys?: (string | number)[];

    // 查询条件
    search?: S;
    // 编辑模态窗口，需要将controller通过disposeExpose暴露出来
    modal?: Ref<ModalExpose<V, T, ModalController<V, T, ModalConfig<V, T>, CrudApi<V, T, any>>>>;
}

export default class CrudController<V extends Named, T, S extends ObjectSearch,
    CONFIG extends CrudConfig<V, T, S>, API extends CrudApi<V, T, S>> {
    // 模板构造参数
    protected clazzV?: ClassConstructor<V>;
    protected clazzT?: ClassConstructor<T>;
    protected clazzS?: ClassConstructor<S>;
    protected clazzAPI?: ClassConstructor<API>;

    protected readonly api: API;
    protected readonly sort = new Sort();

    public readonly config: CONFIG;

  constructor(config: CONFIG) {
        this.config = config;
        // 初始化API
        this.api = new this.clazzAPI!;
        this.api.clazzV = this.clazzV;
        this.api.clazzT = this.clazzT;
        // 初始化config中的变量
        config.loading = false;
        config.page ??= new Page<V>();
        config.contents = [];
        config.selectedRows = [];
        config.selectedRowKeys = [];
        config.search ??= this.clazzS && new this.clazzS;
    }

    @BindThis
    public onActionClick(action: string, record: V, column: ActionColumn, item: ActionItem) {
        const name = this.capitalize(action);
        // @ts-ignore
        const actionMethod = this[`on${name}Click`];
        actionMethod && actionMethod(record);
    }

    @BindThis
    public onCrudClick(crud: string) {
        const name = this.capitalize(crud);
        // @ts-ignore
        const crudMethod = this[`on${name}Click`];
        crudMethod && crudMethod();
    }

    public load() {
        const config = this.config;
        const pageable = new Pageable();
        // 前端的翻页对象，是从1开始的，而后端是从0开始的，因此在回传时，需要减1
        pageable.page = config.page!.number >= 1 ? config.page!.number - 1 : 0;
        pageable.size = config.page!.size;
        pageable.sort = this.sort.toString();

        config.loading = true;
        this.loadData(config.search!, pageable).then(data => {
            // 如果查询返回的是数组，说明需要进行本地分页
            const page = Array.isArray(data) ? this.localSearch(data, config.search!, pageable) : data;
            // 后端从0开始，前端从1开始；前端的0表示没有数据，因此在总数量>0时，number+1
            if (page.totalElements > 0) {
                page.number = page.number + 1;
            }
            config.page = page;
            config.loading = false;
        }).catch(() => config.loading = false);
    }

    @BindThis
    public onAddClick() {
        unref(this.config.modal)?.controller.add(new this.clazzT!);
    }

    @BindThis
    public onEditClick(record: V) {
        unref(this.config.modal)?.controller.edit(record);
    }

    @BindThis
    public onSearchClick() {
        this.config.page!.number = 0;
        this.load();
    }

    @BindThis
    public onRefreshClick() {
        this.load();
    }

    @BindThis
    public onPageClick() {
        this.load();
    }

    @BindThis
    public onImportClick() {
    }

    @BindThis
    public onExportClick() {
    }

    @BindThis
    public onTableChange(pagination: TablePaginationConfig, filters: Record<string, FilterValue | null>,
                         sorter: SorterResult<V> | SorterResult<V>[], extra: TableCurrentDataSource<V>) {
        // 表格的变化事件，包括翻页、过滤（暂不提供）和排序，翻页条件是绑定到page和pageable上的，只需要处理排序条件即可
        if (!sorter) {
            sorter = [];
        }
        if (!Array.isArray(sorter)) {
            sorter = [sorter];
        }
        this.sort.orders = sorter.map(s => {
            const column = <CatColumn>s.column;
            const field = column.sortIndex || column.dataIndex;
            return new Order(String(field), s.order === 'descend' ? Direction.desc : Direction.asc);
        });
        this.onSearchClick();
    }

    @BindThis
    public onDeleteClick(remove: V) {
        this.api.deleteById(remove.id).then(() => this.removeRecord(remove.id));
    }

    @BindThis
    public onBatchDeleteClick() {
        const config = this.config;
        this.api.deleteInBatch(config.selectedRowKeys!).then(() => this.removeSelected());
    }

    @BindThis
    public onBatchEnableClick() {
        const config = this.config;
        this.api.enableInBatch(config.selectedRowKeys!).then(() => {
            config.selectedRows!.forEach(row => row.disabled = false);
        });
    }

    @BindThis
    public onBatchDisableClick() {
        const config = this.config;
        this.api.disableInBatch(config.selectedRowKeys!).then(() => {
            config.selectedRows!.forEach(row => row.disabled = true);
        });
    }

    @BindThis
    public onSwitchChange(dataIndex: string, value: boolean, record: V, column: ColumnType) {
        if (dataIndex == "disabled") {
            if (value) {
                this.api.enable(record.id).then(() => record.disabled = false);
            } else {
                this.api.disable(record.id).then(() => record.disabled = true);
            }
        } else {
            this.api.switch(record.id, dataIndex, value).then(() => {
                // @ts-ignore
                record[dataIndex] = !value;
            })
        }
    }

    @BindThis
    public onSelectionChange(selectedRowKeys: (string | number)[], selectedRows: V[]) {
        this.config.selectedRows = selectedRows;
        this.config.selectedRowKeys = selectedRowKeys;
    }

    @BindThis
    public afterSubmit(result: V, record?: V) {
        if (record) {
            this.updateRecord(result);
        } else {
            this.addRecord(result);
        }
    }

    public addRecord(result: V) {
        this.addElement(this.config.page!.content_, result);
        this.config.page!.totalElements_++;
        this.addElement(this.config.contents!, result);
    }

    public addRecords(results: V[]) {
        results.forEach(result => this.addRecord(result));
    }

    public updateRecord(result: Identifier) {
        this.updateElement(this.config.page!.content_, result);
        this.updateElement(this.config.contents!, result);
    }

    public updateRecords(results: Identifier[]) {
        results.forEach(result => this.updateRecord(result));
    }

    /**
     * 从翻页、缓存和选中的记录中，移除指定的记录
     */
    public removeRecord(removeId: number | string) {
        const config = this.config;
        const index = this.removeElement(config.page!.content_, removeId);
        if (index >= 0) {
            config.page!.totalElements_--;
        }
        this.removeElement(config.contents!, removeId);
        // 可能删除的记录，是选中的记录。需要手工从选中内容中，删除这条选中的记录
        this.removeElement(config.selectedRows!, removeId);
        this.removeElement(config.selectedRowKeys!, removeId);
    }

    public removeRecords(removeIds: (number | string)[]) {
        removeIds.forEach(removeId => this.removeRecord(removeId));
    }

    /**
     * 从翻页、缓存和选中的记录中，移除所有选中的记录
     */
    protected removeSelected() {
        const config = this.config;
        const count = this.removeElements(config.page!.content_, config.selectedRowKeys!);
        if (count >= 0) {
            config.page!.totalElements_ -= count;
        }
        this.removeElements(config.contents!, config.selectedRowKeys!);
        // 手动将选中置空，因为清除时，不会触发table的selectionChange事件
        config.selectedRows = [];
        config.selectedRowKeys = [];
    }

    /**
     * 首字母大写
     */
    private capitalize(str: string): string {
        return str.charAt(0).toUpperCase() + str.slice(1)
    }

    /**
     * 加载数据的方法，子类重载，用于不同的数据加载方式
     * 如果返回的是数组，会自动进行前端分页、排序和查询
     */
    protected loadData(search: S, pageable: Pageable): Promise<Page<V> | V[]> {
        return this.api.search(search, pageable).then(data => {
            // 远程翻页，重置本地缓存
            this.config.contents = [];
            return data;
        });
    }

    /**
     * 从指定的数据集中，完成本地过滤、排序和翻页
     */
    protected localSearch(contents: V[], search: S, pageable: Pageable): Page<V> {
        // 查询条件过滤
        const keys = Object.keys(search);
        const result = contents!.filter(content => keys.every(key => {
            // @ts-ignore
            const condition = search[key], value = content[key];
            if (!condition || condition === '') { // 查询条件没有值，直接返回true
                return true;
            }
            if (!value) { // 数据没有值，直接返回false
                return false;
            }
            return String(value).includes(condition);
        }));
        // 排序控制，只实现单个字段的排序
        const sort = Sort.of(pageable.sort);
        if (sort.orders.length > 0) {
            const order = sort.orders[0];
            result.sort((a, b) => {
                // @ts-ignore
                const v1 = a[order.property], v2 = b[order.property];
                const compare = v1 > v2 ? 1 : -1;
                return order.direction == Direction.asc ? compare : -compare;
            });
        }
        // 翻页控制
        const start = pageable.page * pageable.size;
        const content = result.slice(start, start + pageable.size);
        // 组装翻页
        const page = new Page<V>();
        page.content = content;
        page.numberOfElements = result.length;
        page.number = pageable.page;
        page.size = pageable.size;
        page.totalElements_ = result.length;
        return page;
    }

    /**
     * 删除指定id的记录，返回被删除记录的序号
     */
    private removeElement(sources: (V | number | string)[], removeId: number | string): number {
        const index = sources.findIndex(source => {
            if (typeof source === 'number' || typeof source === 'string') {
                return source === removeId;
            } else {
                return source.id === removeId
            }
        });
        if (index >= 0) {
            sources.splice(index, 1);
        }
        return index;
    }

    /**
     * 删除指定一组id的记录，返回被删除记录的数量
     */
    private removeElements(sources: V[], removeIds: (number | string)[]): number {
        return removeIds.map(removeId => this.removeElement(sources, removeId))
            .filter(index => index >= 0).length;
    }

    /**
     * 更新指定的记录，返回被更新记录的序号
     */
    private updateElement(sources: V[], element: Identifier): number {
        const index = sources.findIndex((source) => source.id === element.id);
        if (index >= 0) {
            Object.assign(sources[index], element);
        }
        return index;
    }

    /**
     * 添加记录
     */
    private addElement(sources: V[], element: V) {
        sources.unshift(element);
    }
}